package tool.analyzer
 
trait ExceptionChecker {
	self: Reporter =>

	import tool.analyzer.Symbols._
	import Types._
	import parser.Trees._

	def exceptionCheck(mainProg: Program): Unit = visitAST(mainProg)

	def visitAST(tree: Tree): Unit = {

		def visitASTHelper(checked: List[ExceptionSymbol])(t: Tree): Unit = t match{
			case Program(m, c) =>  
				visitASTHelper(checked)(m)
				c.foreach(g => visitASTHelper(checked)(g))

			case MainObject(id, s) =>
				visitASTHelper(checked)(s)

			case ClassDecl(id, v, m) =>
				m.foreach(g => visitASTHelper(checked)(g))

			case ClassWithExtends(id, sup, v, m) =>
				m.foreach(g => visitASTHelper(checked)(g))

			case md @ MethDecl(id, args, retType, throws, vars, sts, ret) =>
				visitASTHelper(md.getSymbol.exceptionThrow)(sts)

			case While(test, s) =>
				visitASTHelper(checked)(s)

			case If(test, stat) =>
				visitASTHelper(checked)(stat)

			case IfElse(test, stat, eStat) =>
				visitASTHelper(checked)(stat)
				visitASTHelper(checked)(eStat)

            case TryCatchBlock(b, cs, Some(f)) => 
				visitASTHelper(checked)(TryCatchBlock(b, cs, None))
				visitASTHelper(checked)(f)

			case TryCatchBlock(b, cs, None) =>
				val excs = checked ::: (cs map {_.exceptionId.getSymbol.asInstanceOf[ExceptionSymbol]})
				visitASTHelper(excs)(b)
				cs.foreach(g => visitASTHelper(checked)(g))
				
			case Catch(id, ty, body) =>
				visitASTHelper(checked)(body)
    
            case Finally(body) =>
				visitASTHelper(checked)(body)

			case Block(sts) =>
				sts.foreach(g => visitASTHelper(checked)(g))

			case mc @ MethodCall(instanceId, metId, args) =>
				metId.getSymbol.asInstanceOf[MethodSymbol].exceptionThrow.foreach { g => 
						if( !checked.exists ( g.getType isSubTypeOf _.getType))
							error("Unhandled exception : " + g.name, mc)
				}

			case thro @ Throw(expr) =>
				if( !checked.exists( expr.getType isSubTypeOf _.getType))
					error("Unhandled exception", thro)

			case _ =>
		}

		visitASTHelper(Nil)(tree)
	}
}
